﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Gonte.Caching
{
    public class Cache
    {
        #region Constructors

        public Cache(ExpirationPolicy policy)
        {
            ExceptionThrower.ThrowWhenNull("policy", policy);

            expirationPolicy_ = policy;
            policy.Cache = this;
        } 

        #endregion

        #region Methods

        public void Add(string key, Object data)
        {
            lock_.AcquireWriterLock(Timeout.Infinite);
            try
            {
                objects_.Add(key, data);
                expirationPolicy_.NotifyItemAdded(key);
            }
            finally
            {
                lock_.ReleaseWriterLock();
            }
        }

        public void Set(string key, Object data)
        {
            lock_.AcquireWriterLock(Timeout.Infinite);
            try
            {
                if (objects_.ContainsKey(key))
                {
                    objects_[key] = data;
                    expirationPolicy_.NotifyItemSet(key);
                }
                else
                {
                    objects_.Add(key, data);
                    expirationPolicy_.NotifyItemAdded(key);
                }
            }
            finally
            {
                lock_.ReleaseWriterLock();
            }
        }

        public Object Get(string key)
        {
            lock_.AcquireReaderLock(Timeout.Infinite);
            try
            {
                if (!objects_.ContainsKey(key))
                {
                    return null;
                }

                Object obj = objects_[key];
                expirationPolicy_.NotifyItemFetched(key);
                return obj;
            }
            finally
            {
                lock_.ReleaseReaderLock();
            }
        }

        public void Remove(string key)
        {
            lock_.AcquireWriterLock(Timeout.Infinite);
            try
            {
                objects_.Remove(key);
                expirationPolicy_.NotifyItemRemoved(key);
            }
            finally
            {
                lock_.ReleaseWriterLock();
            }
        }

        #endregion

        #region Properties

        public ExpirationPolicy ExpirationPolicy
        {
            get { return expirationPolicy_; }
            set { expirationPolicy_ = value; }
        }

        public int Count
        {
            get 
            {
                lock_.AcquireReaderLock(Timeout.Infinite);
                try
                {
                    return objects_.Count; 
                }
                finally
                {
                    lock_.ReleaseReaderLock();
                }
            }
        }

        public Object this[string key]
        {
            get { return Get(key); }
            set { Set(key, value); }
        }

        #endregion

        #region Fields

        private Dictionary<string, Object> objects_ = new Dictionary<string, Object>();
        private ExpirationPolicy expirationPolicy_;
        ReaderWriterLock lock_ = new ReaderWriterLock();

        #endregion
    }
}
